# -*- coding: utf-8 -*-

import datetime
import random
from optparse import make_option
from django.core.management import call_command
from django.core.management.base import NoArgsCommand
from django.core.management.color import no_style
from django.contrib.auth.models import User


SHOW_STATUS_EVERY_X_PERCENT = 10

SUPER_USER = {
    'username': 'root',
    'password': 'admin',
    'email': 'root@localhost', 
}

STUDENT_USERNAME_PREFIXES = ['a', 'e', 'f', 'g', 'h', 'i', 'm', 'o', 's', 'z']
NUM_STUDENTS = 80000
STUDENT_INDEX_OFFSET = 6000

OPINION_MAX_FAILLED_ATTEMPTS = 5
OPINION_MIN_RESPONSE_TIME = 45
OPINION_MAX_RESPONSE_TIME = 600

QUESTIONNAIRES = [
    {
        'questionnaire': {
            'title': u'Оцените нове студентске wеb сервисе',
            'slug': 'ocenite-nove-studentske-web-servise',
            'min_sample_size': 400,
            'population_filter': "",
        },
        'target_sample_size_in_percent_of_population': 60,
        'questions': [
            {
                'title': u'Оцените функционалност',
                'answers': [
                    u'Одлична, има све што ми треба',
                    u'Врло добра',
                    u'Добра',
                    u'Подношљива',
                    u'Изразито лоша, радије идем код референта',
                ],
                'answer_fitness': [4, 3.5, 2, 1.5, 0.2]
            },
            {
                'title': u'Оцените једноставност коришћења',
                'answers': [
                    u'Изразито једноставно',
                    u'Једноставно',
                    u'Подношљиво',
                    u'Компликовано',
                    u'Изразито компликовано',
                ],
                'answer_fitness': [3.5, 4, 2, 1, 0.3]
            },
            {
                'title': u'Оцените изглед',
                'answers': [
                    u'Одличан',
                    u'Веома добар',
                    u'Добар',
                    u'Подношљив',
                    u'Изузетно лош',
                ],
                'answer_fitness': [3.3, 4, 2.5, 1.2, 0.4]
            },
            {
                'title': u'Којом сте верзијом сервиса задовољнији?',
                'answers': [
                    u'Новом',
                    u'Старом',
                ],
                'answer_fitness': [8, 1]
            },
        ]
    },
    {
        'questionnaire': {
            'title': u'Репродуктивно здравље',
            'slug': 'reproduktivno-zdravlje',
            'min_sample_size': 200,
            'population_filter': "age>=18",
        },
        'target_sample_size_in_percent_of_population': 40,
        'questions': [
            {
                'title': u'Колико често користите заштиту у полним односима?',
                'answers': [
                    u'Увек',
                    u'Често',
                    u'Понекад',
                    u'Ретко',
                    u'Никад',
                ],
                'answer_fitness': [1.3, 3, 1.5, 2.2, 0.8]
            },
            {
                'title': u'Колико често сте у задњих годину дана мењали сексуалне партнере?',
                'answers': [
                    u'Једном у недељу дана и краће',
                    u'Једном у недељу дана до 2 недеље',
                    u'Једном у 2 недеље до месец дана',
                    u'Једном у месец дана до 3 месеца',
                    u'Једном у 3 месеца до 6 месеци',
                    u'Ређе од 6 месеци',
                ],
                'answer_fitness': [0.7, 1, 1.6, 3.4, 6, 8]
            },
        ]
    },
]


def roulette_selection(fitness):
    from bisect import bisect
    
    breakpoints = [fitness[0],]
    for fit in fitness[1:-1]:
        breakpoints.append(fit + breakpoints[-1])
    total = sum(fitness) * random.random()
    return bisect(breakpoints, total)


class Command(NoArgsCommand):
    option_list = NoArgsCommand.option_list + (
        make_option('--noinput', action='store_false', dest='interactive', default=True,
            help='Tells Django to NOT prompt the user for input of any kind.'),
    )
    help = 'Load sample data for master project.'

    def _reset_db(self):
        print 'Reseting the database...'
        call_command('reset_db', interactive=False)
        print ''

    def _create_superuser(self):
        print '\nCreating the superuser...'
        User.objects.create_superuser(**SUPER_USER)
        superuser = User.objects.get(username=SUPER_USER['username'])
        print "Superuser '%s' created successfully with password '%s'." % (
            superuser.username,
            SUPER_USER['password']
        )
        return superuser

    def _create_student_users(self):
        print "\nCreating %d users for students..." % NUM_STUDENTS
        users = []
        SHOW_STATUS_AT = int(NUM_STUDENTS*SHOW_STATUS_EVERY_X_PERCENT/100.0)
        for index in range(NUM_STUDENTS):
            if index!=0 and index % SHOW_STATUS_AT == 0:
                print "Created %d users..." % index
            username = "%s%s" % (
                random.choice(STUDENT_USERNAME_PREFIXES),
                STUDENT_INDEX_OFFSET+index
            )
            user = User.objects.create_user(
                username=username,
                email='%s@localhost' % username,
                password=username
            )
            users.append(user)
        print 'All users for students created successfully. Their passwords are the same as their usernames.'
        return users
    
    def _put_students_in_group(self, users):
        from django.contrib.auth.models import Permission, Group
        from django.contrib.contenttypes.models import ContentType
        from opinion_extractor.questionnaire.models import Questionnaire
        
        group_name = 'Students'
        print "\nCreating permission group '%s' for students..." % group_name
        perm = Permission.objects.get(
            content_type=ContentType.objects.get_for_model(Questionnaire),
            codename='answer_questionnaire'
        )
        group = Group(name=group_name)
        group.save()
        group.user_set = users
        group.permissions.add(perm)
        print "Permission group '%s' created successfully." % group_name
        return group
    
    def _create_initial_student_profiles(self, users):
        from master.student.models import Student

        num_users = len(users)
        print "\nCreating %d initial student profiles..." % num_users
        students = []
        SHOW_STATUS_AT = int(num_users * SHOW_STATUS_EVERY_X_PERCENT / 100.0)
        for index, user in enumerate(users):
            if index!=0 and index % SHOW_STATUS_AT == 0:
                print "Created %d initial student profiles..." % index
            student = Student(
                user=user,
                gender=random.choice(Student.GENDER_CHOICES)[0],
                age=random.choice(Student.AGE_CHOICES)[0],
                year=random.choice(Student.YEAR_CHOICES)[0]
            )
            student.save()
            students.append(student)
        print 'All initial student profiles created successfully.'
        return students
    
    def _create_and_publish_questionnaires(self, superuser):
        from opinion_extractor.questionnaire.models import Questionnaire, Question
        
        print "\nCreating questionnaires..."
        questionnaires = []
        for spec in QUESTIONNAIRES:
            questionnaire = Questionnaire(
                created_by=superuser,
                **(spec['questionnaire'])
            )
            questionnaire.save()
            
            for q in spec['questions']:
                d = q.copy()
                d.pop('answer_fitness')
                Question(questionnaire=questionnaire, **d).save()
            
            # House-keeping and publishing
            questionnaire.publish_status = 'L'
            questionnaire.published_on = datetime.datetime.now()
            questionnaire.num_questions = questionnaire.questions.count()
            questionnaire.save()
            
            questionnaires.append(questionnaire)
        print 'Questionnaires created successfully.'
        return questionnaires
    
    def _give_initial_opinions(self, questionnaires):
        from master.student.models import Student
        from opinion_extractor.opinion.models import Opinion

        num_questionnaires = len(questionnaires)
        for index, questionnaire in enumerate(questionnaires):
            spec = QUESTIONNAIRES[index]
            fitnesses = map(lambda x: x['answer_fitness'], spec['questions'])
            
            q_filter = questionnaire.get_population_filter_as_Q_object()
            query = Student.objects.filter(q_filter)
            target_sample_size = int(query.count() * spec['target_sample_size_in_percent_of_population'] / 100.0)

            print "\nCreating %d initial opinions for questionnaire '%s'..." % (
                target_sample_size,
                questionnaire
            )

            students = query.select_related('user').order_by('?')[:target_sample_size]
            SHOW_STATUS_AT = int(target_sample_size*SHOW_STATUS_EVERY_X_PERCENT/100.0)
            for index, student in enumerate(students):
                if index!=0 and index % SHOW_STATUS_AT == 0:
                    print "Created %d opinions..." % index
                answers_kw = {}
                for index, fitness in enumerate(fitnesses):
                    answers_kw['answer%d' % index] = roulette_selection(fitness)
                Opinion(
                    user=student.user,
                    questionnaire=questionnaire,
                    num_failled_attempts=random.randint(0, OPINION_MAX_FAILLED_ATTEMPTS),
                    responded_at_time=datetime.datetime.now().time(),
                    response_time_sec=random.randrange(
                        OPINION_MIN_RESPONSE_TIME,
                        OPINION_MAX_RESPONSE_TIME
                    ),
                    **answers_kw
                ).save()
            print "Initial opinions for questionnaire '%s' created successfully." % questionnaire
    
    def handle_noargs(self, **options):
        from datetime import datetime
        from django.db import connection, transaction
        from django.conf import settings
        
        # If True the script sucks up >1GB RAM because Django logs *every* query
        settings.DEBUG = False

        self.style = no_style()

        if options.get('interactive'):
            confirm = raw_input("""
You have requested loading of sample data.
This will IRREVERSIBLY DESTROY any data for
ALL applications in the database "%s".
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: """ % settings.DATABASE_NAME)
            if confirm != 'yes':
                print 'Load sample data cancelled.'
                return
        
        start = datetime.now()
        self._reset_db()
        try:
            transaction.enter_transaction_management()
            transaction.managed(True)
            try:
                call_command('syncdb', interactive=False)

                superuser = self._create_superuser()
                users = self._create_student_users()
                self._put_students_in_group(users)
                students = self._create_initial_student_profiles(users)
                questionnaires = self._create_and_publish_questionnaires(superuser)
                transaction.commit()

                self._give_initial_opinions(questionnaires)
                transaction.commit()
            except:
                # All exceptions must be handled here (even string ones).
                transaction.rollback()
                raise
        finally:
            transaction.leave_transaction_management()
        print "\nTotal time: %s" % (datetime.now() - start)
        #TODO: Generisi po nekoliko izmena profila za studente
        #TODO: Generisi promenu misljenja
